#include "stdafx.h"
#include <json/Json.hpp>
#include <json/JsonParser.hpp>
#include <iostream>
#include <limits>
#include <sstream>

using namespace json;
using namespace std;

template <class T1,class T2> 
static T1 cast(const T2& value){
	static wstringstream ss;
	ss.clear();
	ss << value;
	T1 v;
	ss >> v;
	return v;
}


//---------------------------------------------------------
/* StringBuffer */
StringBuffer::StringBuffer() {
    String::reserve(64);
}

void StringBuffer::append(Char c) {	
    if (this->size() == this->capacity()) {
        this->reserve(this->capacity() + 64);
    }
    this->operator +=(c);
}

//---------------------------------------------------------
// JsonToken
JsonToken::JsonToken() : depth(0) {	
    for (int i = this->depth; i >= 0; i--)
        resetLevel(i);
}

void JsonToken::resetLevel(int depth = -1) {	
    depth = depth == -1 ? this->depth : depth;
    this->stack[depth].state = JsonTokenStateIgnoreWords;
    this->stack[depth].savedState = JsonTokenStateStart;
    this->stack[depth].current = NULL;
    this->stack[depth].fieldName = L"";
}


//---------------------------------------------------------
// JsonException

JsonParseException::JsonParseException(JsonTokenError code)
	: errorCode(code),
	JsonException(errorDescription())
{
}


const Char *JsonParseException::s_errorDescriptions[] = {
	L"JsonTokenSuccess",
	L"JsonTokenContinue",
	L"JsonTokenErrorDepth",
	L"JsonTokenErrorParseEof",
	L"JsonTokenErrorParseUnexpected",
	L"JsonTokenErrorParseNull",
	L"JsonTokenErrorParseBoolean",
	L"JsonTokenErrorParseNumber",
	L"JsonTokenErrorParseArray",
	L"JsonTokenErrorParseObjectKeyName",
	L"JsonTokenErrorParseObjectKeySep",
	L"JsonTokenErrorParseObjectValueSep",
	L"JsonTokenErrorParseString",
	L"JsonTokenErrorParseComment"
};

const Char* JsonParseException::errorDescription() {
    return JsonParseException::s_errorDescriptions[errorCode];
}


//---------------------------------------------------------
/* JsonParser */

JsonParser::JsonParser(){
}
JsonParser::~JsonParser(){
	for(vector<JsonObject*>::iterator i = objects.begin();i != objects.end();++ i){
		JsonObject* obj = *i;
		delete obj;
	}
	objects.clear();
}

JsonObject* JsonParser::parse(const String& str) {
	if(str.empty())
		return NULL;
    return parse(str.begin(), str.end());
}

JsonObject* JsonParser::parse(String::const_iterator start,String::const_iterator end) {	
    JsonToken	token;
    JsonObject* obj = NULL;
    Char		chr = *start;
	
    do {
        JsonTokenStackRecord& stackRecord = token.stack[token.depth];
        JsonTokenState& state = stackRecord.state;
        JsonTokenState& savedState = stackRecord.savedState;

        if (start == end) {
            if (token.depth == 0 && state == JsonTokenStateIgnoreWords && savedState == JsonTokenStateFinish) {
            }else {
                throw JsonParseException(JsonTokenContinue);
            }
            break;
        }
        chr = *start ++;

        JsonObject*& current = stackRecord.current;
        String& fieldName = stackRecord.fieldName;
        StringBuffer& buffer = token.buffer;

redo_char :	
        switch (state) {
            case JsonTokenStateIgnoreWords:
                if (isspace(chr)) {
                    //
                }else if (chr == L'/') {
                    buffer.clear();
                    buffer.append(chr);
                    state = JsonTokenStateCommentStart;
                }else {
                    state = savedState;
                    goto redo_char;
                }
                break;
                // JsonTokenStateEatws end.				
            case JsonTokenStateStart:
            {
                switch (chr) {
                    case L'{':
                        // 一个新JsonObject
                        state = JsonTokenStateIgnoreWords;
                        savedState = JsonTokenStateObjectFieldStart;
                        current = new JObject();
						objects.push_back(current);
                        buffer.clear();
                        break;
                    case L'[':
                        // 一个新的JsonArray
                        state = JsonTokenStateIgnoreWords;
                        savedState = JsonTokenStateArray;
                        current = new JArray();
						objects.push_back(current);
                        buffer.clear();
                        break;
                    case L'N':
                    case L'n':
                        state = JsonTokenStateNull;
                        buffer.clear();
                        goto redo_char;
                    case L'"':
                    case L'\'':
                        state = JsonTokenStateString;
                        token.quoter = chr;
                        buffer.clear();
                        break;
                    case L'T':
                    case L't':
                    case L'F':
                    case L'f':
                        state = JsonTokenStateBoolean;
                        buffer.clear();
                        goto redo_char;
					case L'0':
					case L'1':
					case L'2':
					case L'3':
					case L'4':
					case L'6':
					case L'7':
					case L'8':
					case L'9':
                    case L'-':
					case L'+':
					case L'.':
                        state = JsonTokenStateNumber;
                        token.isDouble = false;
                        buffer.clear();
                        goto redo_char;
                    default:
                        throw JsonParseException(JsonTokenErrorParseUnexpected);
                }
                break;
            }
                // JsonTokenStateStart end.			
            case JsonTokenStateFinish:
                if (token.depth == 0) break;
                obj = current;
                token.resetLevel();
                --token.depth;
                --start;
                break;
                //goto redo_char;
                // JsonTokenStateFinish end.
            case JsonTokenStateNull:
            {
                const String n(L"null");
                buffer.append(tolower(chr));

                if (n.find(buffer) == 0) {
                    if (buffer.size() == n.size()) {
                        current = &JNull;
                        state = JsonTokenStateIgnoreWords;
                        savedState = JsonTokenStateFinish;
                    }
                }else {
                    throw JsonParseException(JsonTokenErrorParseNull);
                }
                break;
            }
            case JsonTokenStateCommentStart:
                if (chr == L'*') {
                    state = JsonTokenStateComment;
                }else if (chr == L'/') {
                    state = JsonTokenStateCommentEndOfLine;
                }else {
                    throw JsonParseException(JsonTokenErrorParseUnexpected);
                }
                buffer.append(chr);
                break;
            case JsonTokenStateComment:
                if (chr == L'*') state = JsonTokenStateCommentEnd;
                buffer.append(chr);
                break;
            case JsonTokenStateCommentEndOfLine:
                if (chr == L'\n') {
                    state = JsonTokenStateIgnoreWords;
                }else {
                    buffer.append(chr);
                }
                break;
            case JsonTokenStateCommentEnd:
                buffer.append(chr);
                if (chr == L'/') {
                    state = JsonTokenStateIgnoreWords;
                }else {
                    state = JsonTokenStateComment;
                }
                break;
            case JsonTokenStateString:
                if (chr == token.quoter) {
                    current = new JString(buffer);
					objects.push_back(current);
                    savedState = JsonTokenStateFinish;
                    state = JsonTokenStateIgnoreWords;
                }else if (chr == L'\\') {
                    savedState = JsonTokenStateString;
                    state = JsonTokenStateStringEscape;
                }else {
                    buffer.append(chr);
                }
                break;
				
            case JsonTokenStateStringEscape:
                switch (chr) {
                    case L'"':
                    case L'\\':
                    case L'/':
                        buffer.append(chr);
                        state = savedState;
                        break;
                    case L'b':
                        buffer.append(L'\b');
                        state = savedState;
                        break;
                    case L'n':
                        buffer.append(L'\n');
                        state = savedState;
                        break;
                    case L'r':
                        buffer.append(L'\r');
                        state = savedState;
                        break;
                    case L't':
                        buffer.append(L'\t');
                        state = savedState;
                        break;
                    case L'u':
                        token.ucs_char = 0;
                        state = JsonTokenStateEscapeUnicode;
                        break;
                    default:
                        throw JsonParseException(JsonTokenErrorParseString);
                }
                break;				
            case JsonTokenStateEscapeUnicode:
                //TODO:处理unicode
                break;
            case JsonTokenStateBoolean:
            {
                const static String t(L"true");
                const static String f(L"false");
                buffer.append(tolower(chr));

                if (t.find(buffer) == 0) {
                    if (buffer.size() == t.size()) {
                        current = &JTrue;
                        state = JsonTokenStateIgnoreWords;
                        savedState = JsonTokenStateFinish;
                    }
                }else if (f.find(buffer) == 0) {
                    if (buffer.size() == f.size()) {
                        current = &JFalse;
                        state = JsonTokenStateIgnoreWords;
                        savedState = JsonTokenStateFinish;
                    }
                }else {
                    throw JsonParseException(JsonTokenErrorParseBoolean);
                }
                break;
            }			
            case JsonTokenStateNumber:
            {
                const static String number = L"0123456789.+-e";
                if (chr && number.find(chr) != string::npos) {
                    buffer.append(chr);
                    if (chr == L'.' || chr == L'e') token.isDouble = true;
                }else {
                	if(token.isDouble)
                		current = new JDouble(cast<double>(buffer));
                	else
                		current = new JInt(cast<int>(buffer));
					objects.push_back(current);
					state = JsonTokenStateIgnoreWords;
                    savedState = JsonTokenStateFinish;
                    goto redo_char;
                }
                break;
            }
            case JsonTokenStateArray:
                if (chr == L']') {
                    savedState = JsonTokenStateFinish;
                    state = JsonTokenStateIgnoreWords;
                }else {
                    if (token.depth >= JSON_TOKEN_MAX_DEPTH - 1) {
                        throw JsonParseException(JsonTokenErrorDepth);
                    }
                    state = JsonTokenStateArrayAdd;
                    ++token.depth;
                    token.resetLevel();
                    --start;
                    //goto redo_char;
                }
                break;
            case JsonTokenStateArrayAdd:
                ((JArray *)current)->add(obj);
                savedState = JsonTokenStateArraySep;
                state = JsonTokenStateIgnoreWords;
                buffer.clear();
                goto redo_char;
			case JsonTokenStateArraySep :
				{
					if (chr == L']') {
						savedState = JsonTokenStateFinish;
						state = JsonTokenStateIgnoreWords;
					}else if (chr == L',') {
						savedState = JsonTokenStateArray;
						state = JsonTokenStateIgnoreWords;
					}else {
						throw JsonParseException(JsonTokenErrorParseArray);
					}
					break;
				}
				
				
                //-----------------------------------------
				/* 处理属性名称部分 */
            case JsonTokenStateObjectFieldStart:
				/* Json属性名称开始 */
                if (chr == L'}') {
                    savedState = JsonTokenStateFinish;
                    state = JsonTokenStateIgnoreWords;
                }else if (chr == L'"' || chr == L'\'') {
                    token.quoter = chr;
                    state = JsonTokenStateObjectField;
                }else {
                    throw JsonParseException(JsonTokenErrorParseObjectKeyName);
                }
                break;
            case JsonTokenStateObjectField:
				{
					/* Json属性名称 */
					if (chr == token.quoter) {
						fieldName = buffer;
						savedState = JsonTokenStateObjectFieldEnd;
						state = JsonTokenStateIgnoreWords;
					}else if (chr == L'\\') {
						savedState = JsonTokenStateObjectField;
						state = JsonTokenStateStringEscape;
					}else {
						buffer.append(chr);
					}
					break;
				}
            case JsonTokenStateObjectFieldEnd:
				/* Json属性名称结束 */
                if (chr == L':') {
                    savedState = JsonTokenStateObjectValue;
                    state = JsonTokenStateIgnoreWords;
                }else {
                    throw JsonParseException(JsonTokenErrorParseObjectKeySep);
                }
                break;

				/* 属性名称处理部分结束 */
				//-----------------------------------------				
            case JsonTokenStateObjectValue:
                if (token.depth >= JSON_TOKEN_MAX_DEPTH - 1) {
                    throw JsonParseException(JsonTokenErrorDepth);
                }
                state = JsonTokenStateObjectValueAdd;
                ++token.depth;
                token.resetLevel();
                --start;
                break;
            case JsonTokenStateObjectValueAdd:
                // 添加fieldName和value到JsonObject
                ((JObject *)current)->set(fieldName,obj);
                savedState = JsonTokenStateObjectSep;
                state = JsonTokenStateIgnoreWords;
                buffer.clear();
                goto redo_char;
            case JsonTokenStateObjectSep:
                if (chr == L'}') {
                    savedState = JsonTokenStateFinish;
                    state = JsonTokenStateIgnoreWords;
                }else if (chr == L',') {
                    savedState = JsonTokenStateObjectFieldStart;
                    state = JsonTokenStateIgnoreWords;
                }else {
                    throw JsonParseException(JsonTokenErrorParseObjectValueSep);
                }
                break;
            default:
                break;
        }


    } while (start != end);	

    if (token.stack[token.depth].state != JsonTokenStateFinish && token.stack[token.depth].savedState != JsonTokenStateFinish) {
        throw JsonParseException(JsonTokenErrorParseEof);
    }
	return token.stack[token.depth].current;
}